package com.qsw.crashcollectionlib.utils;

import android.util.Log;

import com.qsw.crashcollectionlib.bean.ExceptionMessage;
import com.qsw.crashcollectionlib.core.CrashExceptionProcesser;
import com.qsw.crashcollectionlib.core.ExceptionTask;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;

/**
 * 外部调用入口
 * <p>
 * <p>
 * 需要权限：
 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} 以及 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
 * <p>
 * <p>
 * 使用：
 * <p>
 * 1、使用自定Application并继承自{@link com.qsw.crashcollectionlib.view.CrashApplication}
 * <p>
 * 2、调用{@link ExceptionLogUtils}中的接口进行日志读写
 */
public class ExceptionLogUtils {
    /**
     * debug模式下才会保存debug日志
     */
    private static boolean mDebug = true;

    /**
     * 当前是否为debug模式
     *
     * @return 是否为debug模式
     */
    public static boolean isDebug() {
        return mDebug;
    }

    /**
     * 设置是否为debug模式
     *
     * @param debug 是否为debug模式
     */
    public static void setDebug(boolean debug) {
        ExceptionLogUtils.mDebug = debug;
    }

    /**
     * 向默认的存储路径以及日志文件写入一个异常信息
     *
     * @param ex 要写入文件的异常信息
     */
    public static void saveException2Log(Throwable ex) {
        saveException2Log(ex, null, null);
    }

    /**
     * 向指定的日志文件写入一个异常信息
     *
     * @param ex      要写入文件的异常信息
     * @param logPath 指定的日志文件路径
     * @param logName 置顶的日志文件名
     */
    public static void saveException2Log(Throwable ex, String logPath, String logName) {
        StringBuilder sb = new StringBuilder();
        // 写入异常名
        sb.append(ex.getClass().getCanonicalName() + " :  ");
        // 写入异常信息
        sb.append(ex.getMessage() + "\n");
        StackTraceElement[] stackTrace = ex.getStackTrace();
        // 写入异常方法调用栈
        for (StackTraceElement stackTraceElement : stackTrace) {
            sb.append("\t" + stackTraceElement.toString() + "\n");
        }
        saveException2Log(sb.toString(), logPath, logName);
    }

    /**
     * 向默认的存储路径以及日志文件写入一个字符串
     *
     * @param str 要写入文件的字符串
     */
    public static void saveException2Log(String str) {
        saveException2Log(str, null, null);
    }

    /**
     * 向指定的日志文件写入一个字符串
     *
     * @param str     要写入文件的字符串
     * @param logPath 指定的日志文件路径
     * @param logName 置顶的日志文件名
     */
    public static void saveException2Log(String str, String logPath, String logName) {
        saveException2Log(str, logPath, logName, false);
    }

    /**
     * 向默认的存储路径以及日志文件写入一个字符串，精简插入，除自动添加时间外不额外添加修饰内容
     *
     * @param str 要写入文件的字符串
     */
    public static void saveSimpleException2Log(String str) {
        saveSimpleException2Log(str, null, null);
    }

    /**
     * 向指定的日志文件写入一个字符串，精简插入，除自动添加时间外不额外添加修饰内容
     *
     * @param str     要写入文件的字符串
     * @param logPath 指定的日志文件路径
     * @param logName 置顶的日志文件名
     */
    public static void saveSimpleException2Log(String str, String logPath, String logName) {
        saveException2Log(str, logPath, logName, true);
    }

    /**
     * 向默认的存储路径以及日志文件写入一个debug字符串
     *
     * @param str 要写入文件的debug字符串
     */
    public static void saveDebugException2Log(String str) {
        if (mDebug) {
            saveException2Log(str, null, null);
        }
    }

    /**
     * 向指定的日志文件写入一个debug字符串
     *
     * @param str     要写入文件的debug字符串
     * @param logPath 指定的日志文件路径
     * @param logName 指定的日志文件名
     */
    public static void saveDebugException2Log(String str, String logPath, String logName) {
        if (mDebug) {
            saveException2Log(str, logPath, logName, false);
        }
    }

    /**
     * 向默认的存储路径以及日志文件写入一个debug字符串，精简插入，除自动添加时间外不额外添加修饰内容
     *
     * @param str 要写入文件的debug字符串
     */
    public static void saveDebugSimpleException2Log(String str) {
        if (mDebug) {
            saveSimpleException2Log(str, null, null);
        }
    }

    /**
     * 向指定的日志文件写入一个debug字符串，精简插入，除自动添加时间外不额外添加修饰内容
     *
     * @param str     要写入文件的debug字符串
     * @param logPath 指定的日志文件路径
     * @param logName 指定的日志文件名
     */
    public static void saveDebugSimpleException2Log(String str, String logPath, String logName) {
        if (mDebug) {
            saveException2Log(str, logPath, logName, true);
        }
    }

    /**
     * 向指定的日志文件写入一个字符串
     *
     * @param str        要写入文件的字符串
     * @param logPath    指定的日志文件路径
     * @param logName    置顶的日志文件名
     * @param simpleSave 不加过多修饰，只精简添加日志内容
     */
    private static void saveException2Log(String str, String logPath, String logName, boolean simpleSave) {
        ExceptionMessage em = new ExceptionMessage(null, str, System.currentTimeMillis(), logPath, logName, simpleSave, true);
        ExceptionTask.getInstance().saveException2Log(em);
    }

    /**
     * 清除超过指定日期前的日志文件
     *
     * @param time 时间戳，该时间戳前创建的文件将被删除
     */
    public static void clearCrashLogFileOutDate(long time) {
        clearCrashLogFileOutDate(time, CrashExceptionProcesser.getDefaultLogPath());
    }

    /**
     * 清除指定路径中超过指定日期前的日志文件，以最后一次修改时间为准
     *
     * @param time 时间戳，该时间戳前修改的文件将被删除
     * @param path 指定的日志文件路径
     */
    public static void clearCrashLogFileOutDate(long time, String path) {
        File file = new File(path);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        if (files == null || files.length < 1) {
            return;
        }
        for (File logFile : files) {
            if (logFile.lastModified() < time) {
                logFile.delete();
            }
        }
    }

    /**
     * 若默认目录中的日志总大小超过指定大小，则以时间最早的开始删除，直至大小下降到指定以下，以最后一次修改时间为准
     *
     * @param maxSize  最大大小，单位byte
     * @param needSize 若超过maxSize则降到needSize以下，单位byte，必须小于maxSize
     */
    public static void clearCrashLogFileIfOutFullSize(long maxSize, long needSize) {
        clearCrashLogFileIfOutFullSize(maxSize, needSize, CrashExceptionProcesser.getDefaultLogPath());
    }

    /**
     * 若指定目录中的日志总大小超过指定大小，则以时间最早的开始删除，直至大小下降到指定以下，以最后一次修改时间为准
     *
     * @param maxSize  最大大小，单位byte
     * @param needSize 若超过maxSize则降到needSize以下，单位byte，必须小于maxSize
     * @param path     指定的日志文件路径
     */
    public static void clearCrashLogFileIfOutFullSize(long maxSize, long needSize, String path) {
        if (maxSize <= needSize) {
            return;
        }
        File file = new File(path);
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        if (files == null || files.length < 1) {
            return;
        }
        Arrays.sort(files, mFileComparator);
        long size = 0;
        int needSizeIndex = -1;
        boolean reachMaxSize = false;
        for (int i = 0; i < files.length; i++) {
            size += files[i].length();
            if (needSizeIndex < 0 && size > needSize) {
                needSizeIndex = i;
            }
            if (size > maxSize) {
                reachMaxSize = true;
                break;
            }
        }
        if (reachMaxSize) {
            for (int i = files.length - 1; i >= needSizeIndex; i--) {
                files[i].delete();
            }
        }
    }

    /**
     * 设置线程池报警阈值
     *
     * @param taskDangerSize 线程池报警阈值
     */
    public void setTaskDangerSize(int taskDangerSize) {
        ExceptionTask.getInstance().setTaskDangerSize(taskDangerSize);
    }

    /**
     * 文件按最后一次修改时间降序排列
     */
    private static Comparator<File> mFileComparator = new Comparator<File>() {
        @Override
        public int compare(File file1, File file2) {
            if (file1.lastModified() < file2.lastModified()) {
                return 1;
            } else if (file1.lastModified() > file2.lastModified()) {
                return -1;
            } else {
                return 0;
            }
        }
    };

}
